Ever since the earliest days of programming, it has been necessary to perform tracing; i.e. feeding into the program data that are known to produce certain results and then tracing the progress of the data and intermediate results through the calculation to identify errors.
In applications programming, such as an income tax calculation, the test data might be a sample income, deductions and expenses, with the contents of the program data being tested to determine if the deductions are calculated correctly
Many programs have a very large number of paths that may be taken, depending on the particular input parameters, and it can be very difficult to identify the source of an error.
The foregoing supports an argument for embedding extensive test facilities when a program is written, such as placing optional print statements or other instructions to make visible intermediate results that are ordinarily not revealed, in order to reduce the time required to debug the errors that will inevitably be made.
In current technology, the integrated circuits that perform data processing operations have several levels of integration and intermediate data in a calculation are often not available on external pins.
On the other hand, the presence of such debugging features requires that the processor execute instructions that are not ordinarily used and therefore reduce the performance of the program.
Those skilled in the art will appreciate that, in some cases, extensive debugging instructions will not reduce program performance because the program performance is limited by some other factor, such as disk access.
In a class of cases, the program operation does depend on speedy execution of instructions and the presence of debugging instructions will have a noticeable effect on system performance.
In the particular case of microcode, the performance of the system in question is usually rather sensitive to the speed of execution because the performance of the microcode affects the execution speed of all the applications programs.
FIG. 2A illustrates in highly simplified form a portion of microcode that contains trace instructions of a first type. This example is presented for the purpose of illustrating one possible approach and is not intended to be realistic or an illustration of proper programming practice.
Instr(N−2) represents a conventional microcode instruction;
Instr(N−1) represents a second conventional microcode instruction
Go to Trace represents a transfer of control to a subroutine 410, denoted at the bottom of the Figure, that collects data that is useful in detecting errors and prints or stores them, after which control resumes at the next conventional micro-instruction. (The term “GoTo” is not machine language, of course, but is pseudo-code used for purposes of illustration.)
Instr(N) represents another conventional instruction
The second Go to Trace transfers control to the same location, so the data printed or stored will be the same.
Instr(N+1)
Instr(N+2) continue the microcode.
In this example, the use of a subroutine avoids duplication of the data transfer and storage step, at the cost of imposing a fixed set of data to be disclosed. In some cases, the data might be insufficient, and in others it might be excessive. The transfer to the subroutine takes time and machine cycles. A conditional transfer, (by inserting an AND gate keying on a debug flag in the hardware chain that carries out the transfer, or any other convenient method) would take less time, but still impose some overhead on the code. Those skilled in the art will also realize that use of a subroutine imposes additional penalties in terms of resource usage and cycles to transfer control.
Various modifications that impart flexibility will be evident, as will the conclusion that this approach consumes many machine cycles and runs the risk of slowing down system performance.
An alternative method is illustrated in FIG. 2B, in which a series of conditional write statements 415 are inserted in the flow of ordinary instructions. In this case, the data to be passed on for inspection may be varied at each trace point, since they are explicitly set out. The conditional nature of the debugging instructions is an option, since a simple transfer within the CPU will usually take no more time than testing for the condition, so there is no time to be saved by the conditional instructions.
The write statements illustrated are more complex than appears. In this art, the microcode must maintain the area it is writing into, and illustratively uses registers to store head/tail pointers, and in addition to doing the write, must do the pointer management. The “write” illustratively consists of 3 or more instructions.
This second method offers greater flexibility at the cost of more programming effort. Which of the two approaches will slow down the microcode more will depend on many factors.
It has happened that the amount of debugging instructions inserted in the microcode during the initial test period caused such a slowing of performance that it was removed from the version shipped with production models of the system. Unfortunately, difficulties with particular applications then required that software patches be written and sent to the customer to address particular issues.
The art could benefit from a method of trace debugging and debugging system that permitted an extensive scrutiny of the system during operation, in order to provide satisfactory debugging, but still loaded down the system sufficiently little that the debugging instructions could be left in the production code.